package com.parking.service.impl;

import com.parking.entity.ParkingLot;
import com.parking.entity.ParkingOrder;
import com.parking.entity.ParkingSpace;
import com.parking.repository.ParkingLotRepository;
import com.parking.repository.ParkingOrderRepository;
import com.parking.repository.ParkingSpaceRepository;
import com.parking.service.ParkingService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

@Service
@Transactional
public class ParkingServiceImpl implements ParkingService {

    private final ParkingLotRepository parkingLotRepository;
    private final ParkingSpaceRepository parkingSpaceRepository;
    private final ParkingOrderRepository parkingOrderRepository;

    public ParkingServiceImpl(ParkingLotRepository parkingLotRepository,
                              ParkingSpaceRepository parkingSpaceRepository,
                              ParkingOrderRepository parkingOrderRepository) {
        this.parkingLotRepository = parkingLotRepository;
        this.parkingSpaceRepository = parkingSpaceRepository;
        this.parkingOrderRepository = parkingOrderRepository;
    }

    @Override
    public ParkingLot createParkingLot(ParkingLot parkingLot) {
        return parkingLotRepository.save(parkingLot);
    }

    @Override
    public List<ParkingLot> getAllParkingLots() {
        return parkingLotRepository.findAll();
    }

    @Override
    public Optional<ParkingLot> getParkingLotById(Long id) {
        return parkingLotRepository.findById(id);
    }

    @Override
    public void deleteParkingLot(Long id) {
        parkingLotRepository.deleteById(id);
    }

    @Override
    public ParkingSpace createParkingSpace(Long parkingLotId, ParkingSpace space) {
        ParkingLot parkingLot = parkingLotRepository.findById(parkingLotId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid parking lot ID"));

        space.setParkingLot(parkingLot);
        ParkingSpace savedSpace = parkingSpaceRepository.save(space);

        // Update available spaces count
        parkingLot.setAvailableSpaces(parkingLot.getAvailableSpaces() + 1);
        parkingLotRepository.save(parkingLot);

        return savedSpace;
    }

    @Override
    public List<ParkingSpace> getParkingSpacesByLotId(Long parkingLotId) {
        return parkingSpaceRepository.findByParkingLotId(parkingLotId);
    }

    @Override
    public List<ParkingSpace> getAvailableSpacesByLotId(Long parkingLotId) {
        return parkingSpaceRepository.findByParkingLotIdAndOccupied(parkingLotId, false);
    }

    @Override
    public ParkingSpace occupySpace(Long spaceId, String licensePlate) {
        ParkingSpace space = parkingSpaceRepository.findById(spaceId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid space ID"));

        if (space.isOccupied()) {
            throw new IllegalStateException("Parking space is already occupied");
        }

        space.setOccupied(true);
        parkingSpaceRepository.save(space);

        // Create parking order
        ParkingOrder order = new ParkingOrder(licensePlate, space);
        parkingOrderRepository.save(order);

        // Update available spaces count
        ParkingLot parkingLot = space.getParkingLot();
        parkingLot.setAvailableSpaces(parkingLot.getAvailableSpaces() - 1);
        parkingLotRepository.save(parkingLot);

        return space;
    }

    @Override
    public ParkingSpace releaseSpace(Long spaceId) {
        ParkingSpace space = parkingSpaceRepository.findById(spaceId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid space ID"));

        if (!space.isOccupied()) {
            throw new IllegalStateException("Parking space is already available");
        }

        space.setOccupied(false);
        parkingSpaceRepository.save(space);

        // Complete parking order
        ParkingOrder order = parkingOrderRepository.findByParkingSpaceIdAndStatus(spaceId, "IN_PROGRESS")
                .orElseThrow(() -> new IllegalStateException("No active order found for this space"));

        order.setExitTime(LocalDateTime.now());
        order.setStatus("COMPLETED");
        order.setFee(calculateFee(order));
        parkingOrderRepository.save(order);

        // Update available spaces count
        ParkingLot parkingLot = space.getParkingLot();
        parkingLot.setAvailableSpaces(parkingLot.getAvailableSpaces() + 1);
        parkingLotRepository.save(parkingLot);

        return space;
    }

    @Override
    public ParkingOrder createOrder(Long spaceId, String licensePlate) {
        ParkingSpace space = parkingSpaceRepository.findById(spaceId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid space ID"));

        if (space.isOccupied()) {
            throw new IllegalStateException("Parking space is already occupied");
        }

        ParkingOrder order = new ParkingOrder(licensePlate, space);
        space.setOccupied(true);

        parkingSpaceRepository.save(space);
        return parkingOrderRepository.save(order);
    }

    @Override
    public ParkingOrder completeOrder(Long spaceId) {
        ParkingSpace space = parkingSpaceRepository.findById(spaceId)
                .orElseThrow(() -> new IllegalArgumentException("Invalid space ID"));

        ParkingOrder order = parkingOrderRepository.findByParkingSpaceIdAndStatus(spaceId, "IN_PROGRESS")
                .orElseThrow(() -> new IllegalStateException("No active order found for this space"));

        order.setExitTime(LocalDateTime.now());
        order.setStatus("COMPLETED");
        order.setFee(calculateFee(order));

        space.setOccupied(false);
        parkingSpaceRepository.save(space);

        return parkingOrderRepository.save(order);
    }

    @Override
    public List<ParkingOrder> getOrdersByLicensePlate(String licensePlate) {
        return parkingOrderRepository.findByLicensePlate(licensePlate);
    }

    @Override
    public double calculateFee(ParkingOrder order) {
        if (order.getExitTime() == null) {
            return 0.0;
        }

        Duration duration = Duration.between(order.getEntryTime(), order.getExitTime());
        long hours = duration.toHours();
        if (duration.toMinutes() % 60 > 0) {
            hours++; // 不足一小时按一小时计算
        }

        return Math.max(5.0, hours * 5.0); // 最低收费5元，每小时5元
    }
}